using System;
using System.Collections;
using System.Text;

namespace Utility {
	public class Util {
		public static string implode(ArrayList l) {
			return implode(l.ToArray(), ",");
		}
		public static string implode(ArrayList l, string del) {
			return implode(l.ToArray(), del);
		}

		public static string implode(object[] l) {
			return implode(l, ",");
		}
		public static string implode(object[] l, string del) {
			string s = "";
			bool first = true;
			foreach(object o in l) {
				if(!first) s += del;
				s += o;
				first = false;
			}
			return s;
		}

		public static string format(string s, Hashtable data) {
			foreach(DictionaryEntry entry in data) {
				s = s.Replace((string)entry.Key, entry.Value as string);
			}
			return s;
		}

		public static string[] formatArray(string s, Hashtable data) {
			string[] items = new string[data.Count];
			int i=0;
			foreach(string item_format in s.Split(' ')) {
				string item = item_format;
				foreach(DictionaryEntry entry in data) {
					item = item.Replace((string)entry.Key, entry.Value as string);
				}
				items[i] = item;
				i++;
			}
			return items;
		}

		/*
		 *	Use of this function is discouraged because of the
		 *  amount of allocations.
		 */
		public static Hashtable group(ArrayList list, string groupby) {
			Hashtable ret = new Hashtable();
			foreach(Hashtable row in list) {
				string key = format(groupby, row);
				if(!ret.Contains(key)) {
					ret[key] = new ArrayList();
				}
				((ArrayList)ret[key]).Add(row);
			}
			return ret;
		}

		public static string itunesFilter(string filter, string filter_fields) {
			ArrayList and_clauses = new ArrayList();
			string[] fields = filter_fields.Split(' ');
			string[] keywords = filter.Split(' ');
			foreach(string keyword in keywords) {
				ArrayList or_clauses = new ArrayList();
				foreach(string field in fields) {
					or_clauses.Add(field + " LIKE '%" + keyword + "%'");
				}
				and_clauses.Add("(" + Util.implode(or_clauses, " OR ") + ")");
			}
			return Util.implode(and_clauses, " AND ");
		}
	}

	public class Html {
		public static string htjs(string url) {
			return "<script type=\"text/javascript\" src=\"" + url + "\"></script>\n";
		}

		public static string htjscript(string code) {
			return "<script type=\"text/javascript\">\n" +
				code +
				"</script>\n";
		}
	}

	public abstract class Javascript {
		public Javascript() {
		}
		
		protected StringBuilder code = new StringBuilder();
		protected StringBuilder cleanupCode = new StringBuilder();
		protected virtual void javascript(string s) {
			code.Append(s);
		}
		protected virtual void jscleanup(string s) {
			cleanupCode.Append(s);
		}
		protected virtual void jsnullify(string s) {
			jscleanup(s + " = null;");
		}

		public abstract void create();

		public override string ToString() {
			create();
			return code.ToString() + cleanupCode.ToString();
		}

		public virtual void substituteVariable(string searchVar, string replaceVar) {
			code.Replace(searchVar, replaceVar);
		}

		public virtual string globalVariables() {
			return "";
		}

		/*	Utility functions */
		private static int gensym_id = 0;
		public static string gensym() {
			gensym_id++;
			return "obj" + gensym_id;
		}

		public static string escape(string stringWhole) {
			return escape("'", stringWhole);
		}
		public static string escape(string stringPart, string stringWhole) {
			stringWhole = stringWhole.Replace("'", "\\'");
			stringWhole = stringWhole.Replace("\n", "");
			return stringWhole;
		}
	}

	public abstract class JavascriptObject : Javascript {
		public JavascriptObject() {
		}

		protected string objName = gensym();

		public string getName() {
			return objName;
		}
	}

	public abstract class JavascriptFunction : Javascript {
		public JavascriptFunction() {
			this.name = "";
			this.inline = true;
		}
		public JavascriptFunction(string name) {
			this.name = name;
			this.inline = false;
		}
		public JavascriptFunction(string name, string[] parameters) {
			this.name = name;
			this.inline = false;
			this.parameters = parameters;
		}

		protected string name;
		protected bool inline;
		protected string[] parameters;

		protected override void javascript(string s) {
			code.Append(s + "\n");
		}

		public override string ToString() {
			create();
			string ret = code.ToString();
			if(!inline) {
				ret = "function " + name + "(" + Util.implode(parameters) + ") {\n" +
					code +
					cleanupCode +
					"}\n";
			}
			return ret;
		}
	}

}